# 导入必要的Python库
import pandas as pd  # 用于数据处理和分析
import matplotlib.pyplot as plt  # 用于数据可视化
import numpy as np  # 用于数值计算
import seaborn as sns  # 基于matplotlib的高级数据可视化库
from sklearn.pipeline import Pipeline  # 用于创建机器学习流水线
from sklearn.preprocessing import StandardScaler, PolynomialFeatures  # 数据预处理工具
from sklearn.linear_model import LinearRegression, Ridge  # 线性回归和岭回归模型
from sklearn.metrics import mean_squared_error, r2_score  # 模型评估指标
from sklearn.model_selection import cross_val_score, train_test_split  # 模型选择和数据集划分工具

# 任务1：导入数据集
# ---------------------------------------------------------------------------------------------------
# 设置数据文件的URL路径
filepath = 'https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBMDeveloperSkillsNetwork-DA0101EN-Coursera/medical_insurance_dataset.csv'
# 从URL读取CSV文件，header=None表示第一行不是列名
df = pd.read_csv(filepath, header=None)
# 定义数据集的列名
headers = ["age", "gender", "bmi", "no_of_children", "smoker", "region", "charges"]
# 将自定义列名应用到数据框
df.columns = headers
# 将数据中的问号'?'替换为NaN（缺失值）
df.replace('?', np.nan, inplace = True)

# 任务2：数据清洗和预处理
# ---------------------------------------------------------------------------------------------------
# 打印数据框的基本信息，包括数据类型和缺失值情况
print(df.info())
# smoker是分类属性，用最常见的值替换缺失值
# 计算smoker列中每个值的出现次数，并找到出现次数最多的值
is_smoker = df['smoker'].value_counts().idxmax()
# 用最常见的值替换smoker列中的缺失值
df["smoker"].replace(np.nan, is_smoker, inplace=True)

# age是连续变量，用平均值替换缺失值
# 将age列转换为浮点数类型并计算平均值
mean_age = df['age'].astype('float').mean(axis=0)
# 用平均值替换age列中的缺失值
df["age"].replace(np.nan, mean_age, inplace=True)

# 更新数据类型
# 将age和smoker列转换为整数类型
df[["age","smoker"]] = df[["age","smoker"]].astype("int")

# 再次打印数据框信息，查看数据类型变化
print(df.info())
# 将charges列的值四舍五入到小数点后两位
df[["charges"]] = np.round(df[["charges"]],2)
# 打印数据框的前5行，查看数据清洗后的结果
print(df.head())

# 任务3：探索性数据分析（EDA）
# ---------------------------------------------------------------------------------------------------
# 创建BMI与charges的回归图，显示两者之间的关系
sns.regplot(x="bmi", y="charges", data=df, line_kws={"color": "red"})
# 设置y轴的最小值为0
plt.ylim(0,)
# 显示图形
plt.show()
# 创建吸烟状态与charges的箱线图，比较吸烟者和非吸烟者的费用分布
sns.boxplot(x="smoker", y="charges", data=df)
# 显示图形
plt.show()
# 打印数据框中数值列之间的相关系数矩阵
print(df.corr())

# 任务4：模型开发
# ---------------------------------------------------------------------------------------------------
# 选择smoker作为单一特征变量
X = df[['smoker']]
# 选择charges作为目标变量
Y = df['charges']
# 创建线性回归模型实例
lm = LinearRegression()
# 使用单一特征训练线性回归模型
lm.fit(X,Y)
# 打印模型在训练数据上的R²得分
print(lm.score(X, Y))
# 选择所有特征作为输入变量
Z = df[["age", "gender", "bmi", "no_of_children", "smoker", "region"]]
# 使用所有特征重新训练线性回归模型
lm.fit(Z,Y)
# 打印多特征模型的R²得分
print(lm.score(Z, Y))
# 创建机器学习流水线的步骤：标准化、多项式特征生成、线性回归
Input=[('scale',StandardScaler()), ('polynomial', PolynomialFeatures(include_bias=False)), ('model', LinearRegression())]
# 创建流水线对象
pipe=Pipeline(Input)
# 将特征数据转换为浮点数类型
Z = Z.astype(float)
# 使用流水线训练模型
pipe.fit(Z,Y)
# 使用训练好的流水线进行预测
ypipe=pipe.predict(Z)
# 计算流水线模型的R²得分
print(r2_score(Y,ypipe))

# 任务5：模型优化
# ---------------------------------------------------------------------------------------------------
# 将数据集划分为训练集和测试集，测试集占20%，设置随机种子为1以确保结果可重现
x_train, x_test, y_train, y_test = train_test_split(Z, Y, test_size=0.2, random_state=1)
# 创建岭回归模型实例，设置正则化参数alpha为0.1
RidgeModel=Ridge(alpha=0.1)
# 使用训练数据训练岭回归模型
RidgeModel.fit(x_train, y_train)
# 使用测试数据进行预测
yhat = RidgeModel.predict(x_test)
# 计算岭回归模型在测试集上的R²得分
print(r2_score(y_test,yhat))
# 创建多项式特征生成器，设置多项式次数为2
pr = PolynomialFeatures(degree=2)
# 对训练集特征进行多项式变换
x_train_pr = pr.fit_transform(x_train)
# 对测试集特征进行多项式变换
x_test_pr = pr.transform(x_test)
# 使用多项式特征重新训练岭回归模型
RidgeModel.fit(x_train_pr, y_train)
# 使用多项式特征进行预测
y_hat = RidgeModel.predict(x_test_pr)
# 计算多项式岭回归模型在测试集上的R²得分
print(r2_score(y_test,y_hat))
